package com.qfxl.common;





/*



 */

import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.EnvironmentPBEConfig;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.imageio.ImageIO;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.List;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;


/*
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.12.3</version>
        </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.ant/ant -->
    <!-- 压缩相关 -->
        <dependency>
          <groupId>org.apache.ant</groupId>
          <artifactId>ant</artifactId>
          <version>1.10.5</version>
        </dependency>
     <!-- mail邮箱 -->
        <dependency>
            <groupId>com.sun.mail</groupId>
            <artifactId>javax.mail</artifactId>
            <version>1.6.2</version>
        </dependency>
 */
@Data
@Slf4j
public class CommUtil {
    @SneakyThrows
    public static void main(String[] args) {

    }

    /**
     * Base64字符串转图片
     *
     * @param base64String
     * @param imageFileName
     */
    public static void convertBase64StrToImage(String base64String, String imageFileName) {
        ByteArrayInputStream bais = null;
        try {

            //获取图片类型
            String suffix = imageFileName.substring(imageFileName.lastIndexOf(".") + 1);
            //获取JDK8里的解码器Base64.Decoder,将base64字符串转为字节数组
            byte[] bytes = java.util.Base64.getDecoder().decode(base64String);
            //构建字节数组输入流
            bais = new ByteArrayInputStream(bytes);
            //通过ImageIO把字节数组输入流转为BufferedImage
            BufferedImage bufferedImage = ImageIO.read(bais);
            //构建文件
            File imageFile = new File(imageFileName);
            //写入生成文件
            ImageIO.write(bufferedImage, suffix, imageFile);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (bais != null) {
                    bais.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 随机工具类
     */
    public static class RandomUtils {
        // 默认字符集：数字+小写字母+大写字母
        private static final String DEFAULT_CHARACTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        // 数字字符集
        private static final String NUMERIC_CHARACTERS = "0123456789";
        // 小写字母字符集
        private static final String LOWER_ALPHA_CHARACTERS = "abcdefghijklmnopqrstuvwxyz";
        // 大写字母字符集
        private static final String UPPER_ALPHA_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        // 字母字符集（小写+大写）
        private static final String ALPHA_CHARACTERS = LOWER_ALPHA_CHARACTERS + UPPER_ALPHA_CHARACTERS;
        // 字母数字字符集（数字+字母）
        private static final String ALPHA_NUMERIC_CHARACTERS = NUMERIC_CHARACTERS + ALPHA_CHARACTERS;

        /**
         * nextInt(10) 随机生成0-9的整数
         */
        private static Random RANDOM = new Random();

        /**
         * 获取一个指定范围的随机整数，该整数大于等于最小值，且小于等于最大值。
         *
         * @param min 最小值（包含）
         * @param max 最大值（包含）
         * @return 指定范围内的随机整数
         */
        public static int nextInt(int min, int max) {
            return RANDOM.nextInt(max - min + 1) + min;
        }

        /**
         * 获取一个指定范围的随机浮点数，该浮点数大于等于最小值，且小于等于最大值。
         *
         * @param min 最小值（包含）
         * @param max 最大值（包含）
         * @return 指定范围内的随机浮点数
         */
        public static float nextFloat(float min, float max) {
            return min + RANDOM.nextFloat() * (max - min);
        }


        /**
         * 使用默认字符集生成随机字符串
         * @param length 字符串长度
         * @return 随机字符串
         */
        public static String randomString(int length) {
            return randomString(DEFAULT_CHARACTERS, length);
        }

        /**
         * 使用自定义字符集生成随机字符串
         * @param characters 字符集
         * @param length 字符串长度
         * @return 随机字符串
         */
        public static String randomString(String characters, int length) {
            if (characters == null || characters.isEmpty()) {
                characters = DEFAULT_CHARACTERS;
            }

            if (length <= 0) {
                return "";
            }

            Random random = ThreadLocalRandom.current();
            StringBuilder sb = new StringBuilder(length);

            for (int i = 0; i < length; i++) {
                int index = random.nextInt(characters.length());
                sb.append(characters.charAt(index));
            }

            return sb.toString();
        }

        /**
         * 生成随机数字字符串
         * @param length 字符串长度
         * @return 随机数字字符串
         */
        public static String randomNumeric(int length) {
            return randomString(NUMERIC_CHARACTERS, length);
        }

        /**
         * 生成随机字母字符串（包含大小写）
         * @param length 字符串长度
         * @return 随机字母字符串
         */
        public static String randomAlpha(int length) {
            return randomString(ALPHA_CHARACTERS, length);
        }

        /**
         * 生成随机字母数字字符串（包含数字和大小写字母）
         * @param length 字符串长度
         * @return 随机字母数字字符串
         */
        public static String randomAlphaNumeric(int length) {
            return randomString(ALPHA_NUMERIC_CHARACTERS, length);
        }

        /**
         * 生成随机整数
         * @return 随机整数
         */
        public static int randomInt() {
            return ThreadLocalRandom.current().nextInt();
        }

        /**
         * 生成指定范围内的随机整数 [min, max)
         * @param min 最小值（包含）
         * @param max 最大值（不包含）
         * @return 范围内的随机整数
         */
        public static int randomInt(int min, int max) {
            return ThreadLocalRandom.current().nextInt(min, max);
        }

        /**
         * 生成随机长整数
         * @return 随机长整数
         */
        public static long randomLong() {
            return ThreadLocalRandom.current().nextLong();
        }

        /**
         * 生成指定范围内的随机长整数 [min, max)
         * @param min 最小值（包含）
         * @param max 最大值（不包含）
         * @return 范围内的随机长整数
         */
        public static long randomLong(long min, long max) {
            return ThreadLocalRandom.current().nextLong(min, max);
        }

        /**
         * 生成随机双精度浮点数 [0.0, 1.0)
         * @return 随机双精度浮点数
         */
        public static double randomDouble() {
            return ThreadLocalRandom.current().nextDouble();
        }

        /**
         * 生成指定范围内的随机双精度浮点数 [min, max)
         * @param min 最小值（包含）
         * @param max 最大值（不包含）
         * @return 范围内的随机双精度浮点数
         */
        public static double randomDouble(double min, double max) {
            return ThreadLocalRandom.current().nextDouble(min, max);
        }

        /**
         * 生成随机布尔值
         * @return 随机布尔值
         */
        public static boolean randomBoolean() {
            return ThreadLocalRandom.current().nextBoolean();
        }

        /**
         * 从数组中随机选择一个元素
         * @param array 数组
         * @param <T> 元素类型
         * @return 随机选择的元素
         */
        public static <T> T randomElement(T[] array) {
            if (array == null || array.length == 0) {
                return null;
            }
            return array[ThreadLocalRandom.current().nextInt(array.length)];
        }

        /**
         * 从数组中随机选择多个不重复的元素
         * @param array 数组
         * @param count 要选择的元素数量
         * @param <T> 元素类型
         * @return 随机选择的元素数组
         */
        public static <T> T[] randomElements(T[] array, int count) {
            if (array == null || array.length == 0 || count <= 0) {
                return null;
            }

            if (count >= array.length) {
                return array.clone();
            }

            Random random = ThreadLocalRandom.current();
            T[] result = java.util.Arrays.copyOf(array, count);

            // Fisher-Yates 洗牌算法部分实现
            for (int i = 0; i < count; i++) {
                int randomIndex = i + random.nextInt(array.length - i);
                T temp = array[i];
                array[i] = array[randomIndex];
                array[randomIndex] = temp;
                result[i] = array[i];
            }

            return result;
        }

        /**
         * 生成随机字节数组
         * @param length 数组长度
         * @return 随机字节数组
         */
        public static byte[] randomBytes(int length) {
            if (length <= 0) {
                return new byte[0];
            }

            byte[] bytes = new byte[length];
            ThreadLocalRandom.current().nextBytes(bytes);
            return bytes;
        }
    }

    /**
     * 汉字转拼音工具类
     */
    public static class PinYinUtil{
        /**
         * 拼音
         */
        // 返回中文的首字母
        public static String getPinYinHeadChar(String str) {
            String convert = "";
            for (int j = 0; j < str.length(); j++) {
                char word = str.charAt(j);
                String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(word);
                if (pinyinArray != null) {
                    convert += pinyinArray[0].charAt(0);
                } else {
                    convert += word;
                }
            }
            return convert;
        }


        // 将汉字转换为全拼
        public static String getPingYin(String src) {

            char[] t1 = null;
            t1 = src.toCharArray();
            String[] t2 = new String[t1.length];
            HanyuPinyinOutputFormat t3 = new HanyuPinyinOutputFormat();

            t3.setCaseType(HanyuPinyinCaseType.LOWERCASE);
            t3.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
            t3.setVCharType(HanyuPinyinVCharType.WITH_V);
            String t4 = "";
            int t0 = t1.length;
            try {
                for (int i = 0; i < t0; i++) {
                    // 判断是否为汉字字符
                    if (java.lang.Character.toString(t1[i]).matches(
                            "[\\u4E00-\\u9FA5]+")) {
                        t2 = PinyinHelper.toHanyuPinyinStringArray(t1[i], t3);
                        t4 += t2[0];
                    } else{
                        t4 += java.lang.Character.toString(t1[i]);
                    }

                }
                // System.out.println(t4);
                return t4;
            } catch (BadHanyuPinyinOutputFormatCombination e1) {
                e1.printStackTrace();
            }
            return t4;
        }

    }
    /**
     * 文件工具类
     */
    public static class FileUtil {

        public static void main(String[] args) {
            try {
//                String relativeFilePath = CommUtil.FileUtil.getRelativeFilePath("filePath", file);
//                Map<String, Object> map = CommUtil.FileUtil.uploadFie(file, filePath, relativeFilePath);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * @param resourcesPath 资源文件夹
         * @param targetPath    目的压缩文件保存路径
         * @param targetName    压缩包文件名称
         * @return void
         * @throws Exception
         * @desc 将源文件/文件夹生成指定格式的压缩文件,格式zip
         */
        public static void compressedFile(String resourcesPath, String targetPath, String targetName) throws Exception {
            File resourcesFile = new File(resourcesPath);
            File targetFile = new File(targetPath);

            if (!targetFile.exists()) {
                targetFile.mkdirs();
            }

            FileOutputStream outputStream = new FileOutputStream(targetPath + "/" + targetName + ".zip");
            CheckedOutputStream cos = new CheckedOutputStream(outputStream, new CRC32());

            ZipOutputStream out = new org.apache.tools.zip.ZipOutputStream(cos);

            createCompressedFile(out, resourcesFile, "");
            out.close();

        }

        /**
         * @param resourcesPath 资源文件夹
         * @param targetPath    目的压缩文件保存路径
         * @return void
         * @throws Exception
         * @desc 将源文件/文件夹生成指定格式的压缩文件,格式zip
         */
        public static void compressedFile(String resourcesPath, String targetPath) throws Exception {
            File resourcesFile = new File(resourcesPath);
            File targetFile = new File(targetPath);

            if (!targetFile.exists()) {
                targetFile.mkdirs();
            }

            FileOutputStream outputStream = new FileOutputStream(targetPath + ".zip");
            CheckedOutputStream cos = new CheckedOutputStream(outputStream, new CRC32());

            ZipOutputStream out = new org.apache.tools.zip.ZipOutputStream(cos);

            createCompressedFile(out, resourcesFile, "");
            out.close();
            outputStream.close();
            cos.close();

        }

        /**
         * @param out  输出流
         * @param file 目标文件
         * @return void
         * @throws Exception
         * @desc 生成压缩文件。
         * 如果是文件夹，则使用递归，进行文件遍历、压缩
         * 如果是文件，直接压缩
         */
        public static void createCompressedFile(ZipOutputStream out, File file, String dir) throws Exception {
            //如果当前的是文件夹，则进行进一步处理
            if (file.isDirectory()) {
                //得到文件列表信息
                File[] files = file.listFiles();
                //将文件夹添加到下一级打包目录
                out.putNextEntry(new ZipEntry(dir + "/"));

                dir = dir.length() == 0 ? "" : dir + "/";

                //循环将文件夹中的文件打包
                for (int i = 0; i < files.length; i++) {
                    createCompressedFile(out, files[i], dir + files[i].getName());
                }
            } else {   //当前的是文件，打包处理
                //文件输入流
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
                ZipEntry entry = new ZipEntry(dir);
                out.setEncoding("GBK");
                out.putNextEntry(entry);
                // out.putNextEntry(new ZipEntry(dir));
                //进行写操作
                int j = 0;
                byte[] buffer = new byte[1024];
                while ((j = bis.read(buffer)) > 0) {
                    out.write(buffer, 0, j);
                }
                //关闭输入流
                bis.close();
            }
        }

        /**
         * 文件上传
         *
         * @param multipartFile 文件
         * @param baseFilePath  基础路径 eg: D:source/xxx/xx/
         * @param filePath      文件的相对路径 eg:png/xxxxxx.png
         * @return 上传成功或失败 成功包含文件的相对路径 和 名称
         */
        public static Map<String, Object> uploadFie(MultipartFile multipartFile,
                                                    String baseFilePath, String filePath) {
            Map<String, Object> restMap = new HashMap<>();
            try {
                if (multipartFile == null || multipartFile.isEmpty()) {
                    throw new RuntimeException("请上传文件");
                } else {
                    String fileName = multipartFile.getOriginalFilename();
                    if (fileSecurityVerify(multipartFile)) {
                        multipartFile.transferTo(new File(baseFilePath + filePath));
                    } else {
                        throw new RuntimeException("文件上传失败！0001");
                    }
                    restMap.put("name", fileName);
                }
            } catch (Exception e) {
                CommUtil.ExceptionUtil.getStackTraceInfo(e);
                throw new RuntimeException("文件上传失败！0001");
            }
            restMap.put("path", filePath);
            return restMap;
        }

        private static final List<String> ALLOWED_MIME_TYPES = Arrays.asList(
                "image/jpeg",
                "image/png",
                "image/gif",
                "video/mp4",
                "video/x-msvideo",
                "image/webp",
                "image/svg+xml",
                "image/bmp",
                "video/x-flv",
                "video/x-matroska",
                "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                "application/vnd.ms-excel",
                "application/pdf"
        );

        /**
         * 根据文件名，设置HttpServletResponse的ContentType
         *
         * @param fileName 文件名
         * @return 结果
         * @author weimingzhong
         * @date 2024/4/3 9:50
         */
        public static String getContentType(String fileName) {
            Optional<MediaType> mediaType = MediaTypeFactory.getMediaType(fileName);
            return String.valueOf(mediaType.orElse(MediaType.APPLICATION_OCTET_STREAM));
        }

        public static boolean isValidMimeType(MultipartFile file) {
            String mimeType = file.getContentType();
            List<String> allowedMimeTypes = ALLOWED_MIME_TYPES;
            return allowedMimeTypes.contains(mimeType);
        }

        public static String getRelativeFilePath(String fileBasePath, MultipartFile file, String... types) {
            String fileName = "";
            //默认
            if (types.length == 0) {
                String originalFilename = file.getOriginalFilename();
                String substring = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
                int i = RandomUtils.nextInt(10000, 99999);
                String s = System.currentTimeMillis() + "_" + i;
                File baseFile = new File(fileBasePath + substring);
                if (!baseFile.exists()) {
                    baseFile.mkdirs();
                }
                fileName = substring + "/" + s + i + "." + substring;
            }
            return fileName;

        }


        /**
         * 对上传的文件进行 安全漏洞校验，防止上传可执行文件，木马等
         * 1、文件类型检查【检查文件扩展名】
         * 使用Apache Tika等工具来检测文件的实际MIME类型
         *
         * @param multipartFile
         * @return
         */
        public static boolean fileSecurityVerify(MultipartFile multipartFile) {
            boolean restBoolean = true;
            if (multipartFile == null || multipartFile.isEmpty()) {
                return false;
            } else {
                /*
                 * 1、文件类型检查【检查文件扩展名】
                 * 使用Apache Tika等工具来检测文件的实际MIME类型
                 * 2、对上传的文件夹设置不可执行（服务器去设置）
                 * 3、文件重命名
                 */
                String originalFilename = multipartFile.getOriginalFilename();
                assert originalFilename != null;
                String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
                // 允许的文件类型
                List<String> allowedExtensions = Arrays.asList(
                        ".jpg", ".jpeg", ".png", ".gif", ".mp4", ".avi", ".webp", ".svg", ".bmp", ".flv", ".mkv", ".docx", ".xlsx", ".xls", ".pdf"
                );
                if (!allowedExtensions.contains(fileExtension)) {
                    log.info("文件类型不被允许");
                    return false;
                }
                if (!isValidMimeType(multipartFile)) {
                    log.info("文件类型不被允许");
                    return false;
                }
            }
            return restBoolean;
        }
    }

    /**
     * 图片文字工具类
     */
    public static class CreateNamePictureUtil {
        //        public static final String BASE64_PREFIX = "data:image/png;base64,";
        public static final String BASE64_PREFIX = "";

        public static String createBase64Avatar(String userName) throws IOException {
            return BASE64_PREFIX + new String(java.util.Base64.getEncoder().encode(generateImg(userName)));
        }

        public static void createStrImg(String text, String imagePath) {
            // 指定输出的图片路径
//            String imagePath = "path/to/your/image.png";

            // 设置字体和字体大小
            Font font = new Font("宋体", Font.PLAIN, 36);

            // 需要转换成图片的字符串
//            String text = "你好，世界！";

            try {
                // 创建一个空白的图片缓冲区
                BufferedImage image = new BufferedImage(text.length() * 50, 80, BufferedImage.TYPE_INT_ARGB);

                // 获取图片的 Graphics2D 对象
                Graphics2D g2d = image.createGraphics();
                // 设置背景色为白色并填充整个图像
                g2d.setColor(Color.WHITE);
                // 设置字体
                g2d.setFont(font);

                // 获取字体的度量信息，用于计算文字的宽度和高度
                FontMetrics fm = g2d.getFontMetrics();

                // 计算文本的宽度和高度
                int textWidth = fm.stringWidth(text);
                int textHeight = fm.getHeight();

                // 文本居中显示
                int x = (image.getWidth() - textWidth) / 2;
                int y = (image.getHeight() + textHeight) / 2;

                // 设置颜色
                g2d.setColor(Color.BLACK);
                // 在图片上绘制文本
                g2d.drawString(text, x, y);

                // 关闭 Graphics2D 对象
                g2d.dispose();

                // 将图片写入文件
                ImageIO.write(image, "png", new File(imagePath));

                System.out.println("Image saved successfully.");

            } catch (IOException e) {
                System.out.println("An error occurred while writing the image to file.");
                e.printStackTrace();
            }
        }

        /**
         * 绘制字体头像 默认大小100*100
         * 如果是英文名，只显示首字母大写
         * 如果是中文名，只显示最后两个字
         *
         * @param name
         * @throws IOException
         */
        public static byte[] generateImg(String name)
                throws IOException {
            int width = 100;
            int height = 100;
            int nameLen = name.length();
            String nameWritten;
            // 如果用户输入的姓名少于等于2个字符，不用截取
            if (nameLen <= 2) {
                nameWritten = name;
            } else {
                // 如果用户输入的姓名大于等于3个字符，截取后面两位
                String first = name.substring(0, 1);
                if (isChinese(first)) {
                    // 截取倒数两位汉字
                    nameWritten = name.substring(nameLen - 2);
                } else {
                    // 截取后面的两个英文字母
                    nameWritten = name.substring(nameLen - 2).toUpperCase();
                }
            }
            nameWritten = name;
            BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

            Graphics2D g2 = (Graphics2D) bi.getGraphics();
            g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                    RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g2.setBackground(getRandomColor());

            g2.clearRect(0, 0, width, height);
            g2.setPaint(Color.WHITE);
            Font font = null;
            // 两个字及以上
            if (nameWritten.length() >= 2) {
                font = new Font("微软雅黑", Font.PLAIN, 24);
                g2.setFont(font);

                String firstWritten = nameWritten.substring(nameWritten.length() - 1, nameWritten.length());
                FontMetrics fm = g2.getFontMetrics(font);
                int textWidth = fm.stringWidth(firstWritten);
                int widthX = (width - textWidth) / 2;
                if (!isChinese(firstWritten)) {
                    String firstWrittenUpper = firstWritten.toUpperCase();
                    g2.drawString(firstWrittenUpper, widthX, 70);
                } else {
                    g2.drawString(firstWritten, widthX, 70);
                }
            }
            // 一个字
            if (nameWritten.length() == 1) {
                // 中文
                if (isChinese(nameWritten)) {
                    font = new Font("微软雅黑", Font.PLAIN, 55);
                    g2.setFont(font);
                    g2.drawString(nameWritten, 25, 70);
                }
                // 英文
                else {
                    font = new Font("微软雅黑", Font.PLAIN, 55);
                    g2.setFont(font);
                    FontMetrics fm = g2.getFontMetrics(font);
                    int textWidth = fm.stringWidth(nameWritten);
                    int widthX = (width - textWidth) / 2;
                    g2.drawString(nameWritten.toUpperCase(), widthX, 100);
                }
            }
            log.info("name is {}", nameWritten);
            BufferedImage rounded = makeRoundedCorner(bi, 99);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ImageIO.write(rounded, "png", byteArrayOutputStream);
            return byteArrayOutputStream.toByteArray();
        }

        /**
         * 判断字符串是否为中文
         *
         * @param str
         * @return
         */
        public static boolean isChinese(String str) {
            String regEx = "[\\u4e00-\\u9fa5]+";
            Pattern p = Pattern.compile(regEx);
            Matcher m = p.matcher(str);
            if (m.find())
                return true;
            else
                return false;
        }

        /**
         * 获得随机颜色
         *
         * @return
         */
        public static Color getRandomColor() {
            String[] beautifulColors =
                    new String[]{"141,224,157", "116,219,216", "120,195,255", "167,157,255", "255,161,217",
                            "252,136,136", "247,199,94"};
            int len = beautifulColors.length;
            Random random = new Random();
            String[] color = beautifulColors[random.nextInt(len)].split(",");
            return new Color(Integer.parseInt(color[0]), Integer.parseInt(color[1]),
                    Integer.parseInt(color[2]));
        }

        /**
         * 图片做圆角处理
         *
         * @param image
         * @param cornerRadius
         * @return
         */
        public static BufferedImage makeRoundedCorner(BufferedImage image, int cornerRadius) {
            int w = image.getWidth();
            int h = image.getHeight();
            BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2 = output.createGraphics();
            g2.setComposite(AlphaComposite.Src);
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setColor(Color.WHITE);
            g2.fill(new RoundRectangle2D.Float(0, 0, w, h, cornerRadius, cornerRadius));
            g2.setComposite(AlphaComposite.SrcAtop);
            g2.drawImage(image, 0, 0, null);
            g2.dispose();
            return output;
        }
    }

    /**
     * 正则校验工具类
     */
    public static class RegexUtil {

        public static void main(String[] args) {
            String pattern = PatternEnum.EMAIL.getPattern();
            String email = "1254769959@qq,com";
            boolean matches = Pattern.matches(pattern, email);
            System.out.println(matches);
        }

        // 定义正则表达式模式
        private static final String PASSWORD_PATTERN =
                "^(?=.*[0-9])" +         // 至少包含一个数字
                        "(?=.*[a-z])" +          // 至少包含一个小写字母
                        "(?=.*[A-Z])" +          // 至少包含一个大写字母
                        "(?=.*[@#$%^&+=])" +     // 至少包含一个特殊字符
                        "(?=\\S+$)" +            // 不能包含空白字符
                        ".{8,16}$";              // 长度在8到16个字符之间

        public enum PatternEnum {


            /**
             * 密码校验
             */
            PASSWORD_COMPLEX(PASSWORD_PATTERN, "password_complex"),


            /**
             * 邮箱
             */
            EMAIL("^(\\w+([-.][A-Za-z0-9]+)*){3,18}@\\w+([-.][A-Za-z0-9]+)*\\.\\w+([-.][A-Za-z0-9]+)*$",
                    "email"),


            /**
             * yyyy-MM-dd HH:mm:ss
             * yyyy-MM-dd
             * yyyy-MM-dd
             * YYYY-MM-ddTHH:mm:ss
             * yyyy-MM-dd'T'HH:mm:ss.SSS'Z'   UTC世界标准时间格式
             */


            /**
             * 时间格式  yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
             */
            UTC_DATE("^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})\\.(\\d{3})Z$",
                    "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),//yyyy-MM-dd'T'HH:mm:ss.SSS'Z'

            /**
             * 时间格式 yyyy-MM-dd HH:mm:ss 校验方式
             */
            FULL_DATE_TIME("^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])\\s+(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$"
                    , "yyyy-MM-dd HH:mm:ss"),// yyyy-MM-dd HH:mm:ss

            /**
             * 时间格式 yyyy-MM-dd 校验方式
             */
            DATE_ONLY("^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "yyyy-MM-dd"),

            /**
             * 时间格式 YYYY-MM-ddTHH:mm:ss 校验方式
             */
            ISO_DATE_TIME("^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$"
                    , "YYYY-MM-ddTHH:mm:ss");//YYYY-MM-ddTHH:mm:ss


            private String pattern;
            private String form;

            PatternEnum(String pattern) {
                this.pattern = pattern;

            }

            PatternEnum(String pattern, String form) {
                this.pattern = pattern;
                this.form = form;
            }

            /**
             * @return 对应的正则
             */
            public String getPattern() {
                return this.pattern;
            }

            public String getForm() {
                return this.form;
            }
        }


    }

    /**
     * 字符串工具类
     */
    public static class StringUtil {

        static String familyName1 = "赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻水云苏潘葛奚范彭郎鲁韦昌马苗凤花方俞任袁柳鲍史唐费岑薛雷贺倪汤滕殷罗毕郝邬安常乐于时傅卞齐康伍余元卜顾孟平"
                + "黄和穆萧尹姚邵湛汪祁毛禹狄米贝明臧计成戴宋茅庞熊纪舒屈项祝董粱杜阮席季麻强贾路娄危江童颜郭梅盛林刁钟徐邱骆高夏蔡田胡凌霍万柯卢莫房缪干解应宗丁宣邓郁单杭洪包诸左石崔吉"
                + "龚程邢滑裴陆荣翁荀羊甄家封芮储靳邴松井富乌焦巴弓牧隗山谷车侯伊宁仇祖武符刘景詹束龙叶幸司韶黎乔苍双闻莘劳逄姬冉宰桂牛寿通边燕冀尚农温庄晏瞿茹习鱼容向古戈终居衡步都耿满弘国文东殴沃曾关红游盖益桓公晋楚闫";
        static String familyName2 = "欧阳太史端木上官司马东方独孤南宫万俟闻人夏侯诸葛尉迟公羊赫连澹台皇甫宗政濮阳公冶太叔申屠公孙慕容仲孙钟离长孙宇文司徒鲜于司空闾丘子车亓官司寇巫马公西颛孙壤驷公良漆雕乐正宰父谷梁拓跋夹谷轩辕令狐段干百里呼延东郭南门羊舌微生公户公玉公仪梁丘公仲公上公门公山公坚左丘公伯西门公祖第五公乘贯丘公皙南荣东里东宫仲长子书子桑即墨达奚褚师吴铭";
        static String girlName = "秀娟英华慧巧美娜静淑惠珠翠雅芝玉萍红娥玲芬芳燕彩春菊兰凤洁梅琳素云莲真环雪荣爱妹霞香月莺媛艳瑞凡佳嘉琼勤珍贞莉桂娣叶璧璐娅琦晶妍茜秋珊莎锦黛青倩婷姣婉娴瑾颖露瑶怡婵雁蓓纨仪荷丹蓉眉君琴蕊薇菁梦岚苑婕馨瑗琰韵融园艺咏卿聪澜纯毓悦昭冰爽琬茗羽希宁欣飘育滢馥筠柔竹霭凝晓欢霄枫芸菲寒伊亚宜可姬舒影荔枝思丽";
        static String boyName = "伟刚勇毅俊峰强军平保东文辉力明永健世广志义兴良海山仁波宁贵福生龙元全国胜学祥才发武新利清飞彬富顺信子杰涛昌成康星光天达安岩中茂进林有坚和彪博诚先敬震振壮会思群豪心邦承乐绍功松善厚庆磊民友裕河哲江超浩亮政谦亨奇固之轮翰朗伯宏言若鸣朋斌梁栋维启克伦翔旭鹏泽晨辰士以建家致树炎德行时泰盛雄琛钧冠策腾楠榕风航弘";

        /**
         * 功能：随机产生姓氏
         *
         * @return
         */
        public static String getFamilyName() {
            String str = "";
            int randNum = new Random().nextInt(2) + 1;
            int strLen = randNum == 1 ? familyName1.length() : familyName2.length();
            int index = new Random().nextInt(strLen);
            if (randNum == 1) {
                str = String.valueOf(familyName1.charAt(index));
            } else {
                str = (index & 1) == 0 ? familyName2.substring(index, index + 2) :
                        familyName2.substring(index - 1, index + 1);
            }
            return str;
        }

        /**
         * 功能：随机产生性别
         *
         * @return
         */
        public static String getSex() {
            int randNum = new Random().nextInt(2) + 1;
            return randNum == 1 ? "男" : "女";
        }

        /**
         * 随机名称
         *
         * @return
         */
        public static String getRandName() {
            String sex = StringUtil.getSex();
            int randNum = new Random().nextInt(2) + 1;
            int strLen = sex.equals("男") ? boyName.length() : girlName.length();
            int index = (randNum & 1) == 0 ? new Random().nextInt(strLen - 1) :
                    new Random().nextInt(strLen);
            String familyNameA = StringUtil.getFamilyName();
            String familyNameB = sex.equals("男") ? boyName.substring(index, index + randNum) :
                    girlName.substring(index, index + randNum);
            return familyNameA.concat(familyNameB);
        }

        /**
         * 功能：随机产生18-21的整数
         *
         * @return
         */
        public static int getAge() {
            return new Random().nextInt(4) + 18;
        }

        /**
         * 数字金额大写转换，思想先写个完整的然后将如零拾替换成零 要用到正则表达式
         *
         * @param n 数字金额
         * @return 大写
         */
        public static String digitUppercase(double n) {
            String fraction[] = {"角", "分"};
            String digit[] = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
            String unit[][] = {{"元", "万", "亿"}, {"", "拾", "佰", "仟"}};

            String head = n < 0 ? "负" : "";
            n = Math.abs(n);

            String s = "";
            for (int i = 0; i < fraction.length; i++) {
                s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
            }
            if (s.length() < 1) {
                s = "整";
            }
            int integerPart = (int) Math.floor(n);

            for (int i = 0; i < unit[0].length && integerPart > 0; i++) {
                String p = "";
                for (int j = 0; j < unit[1].length && n > 0; j++) {
                    p = digit[integerPart % 10] + unit[1][j] + p;
                    integerPart = integerPart / 10;
                }
                s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;
            }
            return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整");
        }

        /**
         * 判断字符串中是否包含 字符串集合中元素
         *
         * @param str        字符串
         * @param stringList 字符串集合
         * @return 满足就返回true 一个都包含就返回 false
         */
        public static boolean strContainByList(Object str, List<String> stringList) {
            if (str == null) {
                return false;
            }
            return stringList.stream().anyMatch(String.valueOf(str)::contains);
        }
    }

    /**
     * 时间工具类
     */
    public static class TimeUtil {

        public static void main(String[] args) {
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            String endTime = "2027-09-10";
            String startTime = "2024-03-31";
            LocalDate parse = LocalDate.parse(startTime, dateTimeFormatter);
            LocalDate localDate = parse.plusMonths(1).with(TemporalAdjusters.lastDayOfMonth()).plusDays(-1);
//            parse.plus(temporal)
            System.out.println(localDate);
            System.out.println("--------------------------");
            LocalDate startDate = LocalDate.of(2023, 10, 1);
            LocalDate endDate = LocalDate.of(2023, 10, 15);

        }

        /**
         * 字符串时间相减 没有格式规定
         *
         * @param startTimeStr 开始时间
         * @param endTimeStr   结束时间
         * @param unit         返回的单位
         * @return 相差数
         */
        public static Long DateSubtract(String startTimeStr, String endTimeStr,
                                        DateUnit unit) {
            return DateSubtract(startTimeStr, null, endTimeStr, null, unit);
        }

        /**
         * 格林威治时间转化
         *
         * @param intoFormat 输入的时间格式
         * @param outFormat  输出的格式
         * @param dateStr    需要转化的时间字符串
         * @return 转换后的时间字符串
         */
        public static String timeConversionGMT(String intoFormat, String outFormat,
                                               String dateStr) {
            return timeFormatConversion(intoFormat, outFormat, dateStr, "GMT");
        }

        /**
         * 时间格式转化
         *
         * @param intoFormat 输入的时间格式
         * @param outFormat  输出的格式
         * @param dateStr    需要转化的时间字符串
         * @param timeType   特殊处理的类型 (格林威治时间转化)
         * @return 转换后的时间字符串
         */
        public static String timeFormatConversion(String intoFormat, String outFormat,
                                                  String dateStr, String timeType) {
            String returnTime;
            try {
                DateFormat dateFormat = new SimpleDateFormat(intoFormat);
                Date date = dateFormat.parse(dateStr);
                if (timeType.equals("GMT")) {
                    //格林威治时间转化
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(date);
                    calendar.set(Calendar.HOUR, calendar.get(Calendar.HOUR) + 8);
                    date = calendar.getTime();
                }
                returnTime = new SimpleDateFormat(outFormat).format(date.getTime());
            } catch (Exception e) {
                e.printStackTrace();
                returnTime = "";
            }
            return returnTime;
        }

        /**
         * 获取指定时间的月份开始时间和结束时间
         *
         * @param restFormFormatter 返回时间的格式
         * @param times             指定时间，没有就默认当前时间
         * @return 当前时间的 月份范围
         */
        public static List<String> getNowMonthRange(DateTimeFormatter restFormFormatter, String... times) {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            String dateString;
            if (times.length == 0) {
                LocalDate now = LocalDate.now();
                dateString = now.format(formatter);
            } else {
                dateString = times[0];
            }
            // 将字符串转换为 LocalDate 对象
            LocalDate date = LocalDate.parse(dateString, formatter);

            // 从 LocalDate 对象中提取 YearMonth 对象
            YearMonth yearMonth = YearMonth.from(date);

            // 获取该月的第一天和最后一天
            LocalDate firstDayOfMonth = yearMonth.atDay(1);
            LocalDate lastDayOfMonth = yearMonth.atEndOfMonth();

            // 转换为 LocalDateTime 并设置时间（例如，开始时间为 00:00:00，结束时间为 23:59:59）
            LocalDateTime startOfMonth = firstDayOfMonth.atStartOfDay();
            LocalDateTime endOfMonth = lastDayOfMonth.atTime(23, 59, 59);

            List<String> arrayList = new ArrayList<>();
            arrayList.add(startOfMonth.format(restFormFormatter));
            arrayList.add(endOfMonth.format(restFormFormatter));
            return arrayList;
        }


        /**
         * 时间相减
         *
         * @param startTimeStr     开始时间
         * @param startTimeStrForm 开始时间格式
         * @param endTimeStr       结束时间
         * @param endTimeStrForm   结束时间格式
         * @param unit             返回的单位
         * @return 相差数
         */
        public static Long DateSubtract(String startTimeStr, String startTimeStrForm, String endTimeStr,
                                        String endTimeStrForm, DateUnit unit) {
            SimpleDateFormat startSpd;
            SimpleDateFormat endSpd;
            //时间格式为空就给默认的来适配 [yyyy-MM-dd HH:mm:ss]  [yyyy-MM-dd]
            if (StringUtils.isEmpty(startTimeStrForm)) {
                if (startTimeStr.matches(RegexUtil.PatternEnum.DATE_ONLY.getPattern())) {
                    startTimeStrForm = RegexUtil.PatternEnum.DATE_ONLY.getForm();
                } else if (startTimeStr.matches(RegexUtil.PatternEnum.FULL_DATE_TIME.getPattern())) {
                    startTimeStrForm = RegexUtil.PatternEnum.FULL_DATE_TIME.getForm();
                }
            }
            //时间格式为空就给默认的来适配 [yyyy-MM-dd HH:mm:ss]  [yyyy-MM-dd]
            if (StringUtils.isEmpty(endTimeStrForm)) {
                if (endTimeStr.matches(RegexUtil.PatternEnum.DATE_ONLY.getPattern())) {
                    endTimeStrForm = RegexUtil.PatternEnum.DATE_ONLY.getForm();
                } else if (endTimeStr.matches(RegexUtil.PatternEnum.FULL_DATE_TIME.getPattern())) {
                    endTimeStrForm = RegexUtil.PatternEnum.FULL_DATE_TIME.getForm();
                }
            }
            startSpd = new SimpleDateFormat(startTimeStrForm);
            endSpd = new SimpleDateFormat(endTimeStrForm);
            Date endDate;
            Date startDate;

            try {
                endDate = endSpd.parse(endTimeStr);
                startDate = startSpd.parse(startTimeStr);
            } catch (Exception e) {
                return 0L;
            }
            long diff = endDate.getTime() - startDate.getTime();
            long millis = unit.getMillis();
            long sub = diff / millis;
            return sub;
        }

        public enum DateUnit {
            /**
             * 一毫秒
             */
            MS(1),
            /**
             * 一秒的毫秒数
             */
            SECOND(1000),
            /**
             * 一分钟的毫秒数
             */
            MINUTE(SECOND.getMillis() * 60),
            /**
             * 一小时的毫秒数
             */
            HOUR(MINUTE.getMillis() * 60),
            /**
             * 一天的毫秒数
             */
            DAY(HOUR.getMillis() * 24),
            /**
             * 一周的毫秒数
             */
            WEEK(DAY.getMillis() * 7);

            private long millis;

            DateUnit(long millis) {
                this.millis = millis;
            }

            /**
             * @return 单位对应的毫秒数
             */
            public long getMillis() {
                return this.millis;
            }
        }

    }

    public static class PasswordUtils {
        public static void main(String[] args) {
            char a = '!';
            System.out.println((int) a);
        }
    }

    /**
     * JWT 工具类 token
     */
    public static class JwtUtils {
        private static final int MAX_ENCRYPT_BLOCK = 117;
        private static final int MAX_DECRYPT_BLOCK = 128;
        private static String SECRET = "jwt-secret";//创建加密盐

        //过期时间
        private static Long expiration = 24L;
        private static Date expTime = new Date(new Date().getTime() + (1000 * 60 * 120));


        /**
         * .\keytool.exe -genkeypair -alias {别名} -keypass {密钥密码} -keyalg RSA -keysize 1024 -validity 365
         * -keystore D:\cj\fd-alias.keystore -storepass {密码库密码}
         * <p>
         * 根据密钥库生成 公钥密钥文件在当前文件下
         *
         * @param keystorePath 密钥库文件的地址 （是从resource下获取）相对路径 fd-alias.keystore
         * @param alias        别名  fd-alias
         * @param keystorePwd  密码库密码  1024=0x400
         * @param secretKeyPwd 密钥密码 1024=0x400
         */
        public static void setSecretKeyPriPubFileByKeystore(String keystorePath, String alias, String keystorePwd, String secretKeyPwd) {
            try {
                //从resource中文件获取对应的密钥库文件
                InputStream inputStream = ClassLoader.getSystemResourceAsStream(keystorePath);
                //设置 Java密钥库(Java Key Store，JKS)
                KeyStore jks = KeyStore.getInstance("JKS");
                // stream : 加载密钥存储库的输入流，password：用于检查密钥存储库完整性的密码，所使用的密码（生成fd-alias.keystore密码库密码）
                jks.load(inputStream, keystorePwd.toCharArray());
                //关闭流程
                inputStream.close();
                //String alias 密码库的别名, char[] password 密钥的密码
                PrivateKey privateKey = (PrivateKey) jks.getKey(alias, secretKeyPwd.toCharArray());
                PublicKey publicKey = jks.getCertificate(alias).getPublicKey();

                // 保存公钥到文件
                FileOutputStream publicFos = new FileOutputStream("public_key.key");
                publicFos.write(publicKey.getEncoded());
                publicFos.close();

                // 保存私钥到文件
                FileOutputStream privateFos = new FileOutputStream("private_key.key");
                privateFos.write(privateKey.getEncoded());
                privateFos.close();


            } catch (Exception e) {
                String stackTraceInfo = ExceptionUtil.getStackTraceInfo(e);
                log.info(stackTraceInfo);
            }
        }


        public static PrivateKey getPrivateKey() {
            return getPrivateKey("jwt/private_key.key");
        }

        public static PrivateKey getPrivateKey(String filename) {
            try {
                InputStream resourceAsStream = CommUtil.class.getClassLoader().getResourceAsStream(filename);
                DataInputStream dis = new DataInputStream(resourceAsStream);
                byte[] keyBytes = new byte[resourceAsStream.available()];
                dis.readFully(keyBytes);
                dis.close();
                PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
                KeyFactory kf = KeyFactory.getInstance("RSA");
                return kf.generatePrivate(spec);
            } catch (Exception e) {
                CommUtil.ExceptionUtil.getStackTraceInfo(e);
                return null;
            }
        }

        public static PublicKey getPublicKey() {
            return getPublicKey("jwt/public_key.key");
        }

        public static PublicKey getPublicKey(String filename) {
            try {
                InputStream resourceAsStream = CommUtil.class.getClassLoader().getResourceAsStream(filename);
                DataInputStream dis = new DataInputStream(resourceAsStream);
                byte[] keyBytes = new byte[resourceAsStream.available()];
                dis.readFully(keyBytes);
                dis.close();
                X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
                KeyFactory kf = KeyFactory.getInstance("RSA");
                return kf.generatePublic(spec);
            } catch (Exception e) {
                CommUtil.ExceptionUtil.getStackTraceInfo(e);
                return null;
            }
        }

        /**
         * 生成JWT Token
         *
         * @param userInfoJson 用户消息 json串
         */
        public static String createToken(String userInfoJson) {
            try {
                //得到当前的系统时间
                Date currentDate = new Date();
                //根据当前时间计算出过期时间 定死为120分钟，1000毫秒
                //组装JWT的头数据
                Map<String, Object> header = new HashMap<>();
                header.put("alg", "HS256");
                header.put("typ", "JWT");
                String priPath = "jwt/private_key.key";
                PrivateKey privateKey = getPrivateKey(priPath);
                //                String pubPath = "jwt/public_key.key";
                //                PublicKey publicKey = getPublicKey(pubPath);
                String token = Jwts.builder()
                        .setHeader(header)//头
                        .claim("userInfo", userInfoJson)////自定义数据，自定义声明
                        .setIssuedAt(currentDate)//创建时间
//                        .setExpiration(new Date(new Date().getTime() + (1000 )))//过期时间
                        .setExpiration(new Date(new Date().getTime() + (1000 * 60 * 9999)))//过期时间
                        .signWith(SignatureAlgorithm.RS256, privateKey)//使用指定的算法和指定的密钥对构造的JWT签名，生成JWS
                        .compact();
                return token;
            } catch (Exception e) {
                CommUtil.ExceptionUtil.getStackTraceInfo(e);
                return "";
            }
        }




        /**
         * 公钥加密
         *
         * @param publicKey
         * @param str
         * @return
         */
        public static String publicKeyEncryption(PublicKey publicKey, String str) {
            try {
                Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
                cipher.init(1, publicKey);
                // 分段加密
                // URLEncoder编码解决中文乱码问题
                byte[] data = URLEncoder.encode(str, "UTF-8").getBytes("UTF-8");
                System.out.println("data:" + Arrays.toString(data));
                // 加密时超过117字节就报错。为此采用分段加密的办法来加密
                byte[] enBytes = null;
                for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) {
                    // 注意要使用2的倍数，否则会出现加密后的内容再解密时为乱码
                    byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK));
                    enBytes = ArrayUtils.addAll(enBytes, doFinal);
                }
                String outStr = Base64.encodeBase64String(enBytes);
                return outStr;

            } catch (Exception e) {
                String stackTraceInfo = ExceptionUtil.getStackTraceInfo(e);
                log.info(stackTraceInfo);
                return "";
            }
        }

        public static List<String> splitStringByLength(String str, int length) {
            // 创建一个列表来存储结果
            List<String> result = new ArrayList<>();

            // 计算整个字符串可以被分成多少段
            int numSubstrings = (int) Math.ceil((double) str.length() / length);

            // 使用循环来分割字符串
            for (int i = 0; i < numSubstrings; i++) {
                // 计算当前片段的开始位置
                int start = i * length;
                // 如果剩余的字符数量小于指定长度，则结束位置为字符串末尾
                int end = Math.min(start + length, str.length());

                // 截取子字符串并添加到结果列表
                result.add(str.substring(start, end));
            }

            return result;
        }

        /**
         * 公钥加密
         *
         * @param publicKey 公钥
         * @param str       明文
         * @param split     明文的分割标识
         * @return
         */
        public static String publicKeyEncryption(PublicKey publicKey, String str, int size, String split) {
            try {
                // 返回UTF-8编码的解密信息
                StringBuffer sb = new StringBuffer();
                for (String entity : splitStringByLength(str, size)) {
                    String s = publicKeyEncryption(publicKey, entity);
                    sb.append(split).append(s);
                }
                return sb.toString();
            } catch (Exception e) {
                String stackTraceInfo = ExceptionUtil.getStackTraceInfo(e);
                log.info(stackTraceInfo);
                return "";
            }
        }


        public static String privateKeyDecryption(PrivateKey privateKey, String str) {
            try {
                Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
                cipher.init(2, privateKey);
                byte[] data = Base64.decodeBase64(str.getBytes("UTF-8"));
                // 返回UTF-8编码的解密信息
                int inputLen = data.length;
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                int offSet = 0;
                byte[] cache;
                int i = 0;
                // 对数据分段解密
                while (inputLen - offSet > 0) {
                    if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                        cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
                    } else {
                        cache = cipher.doFinal(data, offSet, inputLen - offSet);
                    }
                    out.write(cache, 0, cache.length);
                    i++;
                    offSet = i * 128;
                }
                byte[] decryptedData = out.toByteArray();
                out.close();
                return URLDecoder.decode(new String(decryptedData, "UTF-8"), "UTF-8");
            } catch (Exception e) {
                String stackTraceInfo = ExceptionUtil.getStackTraceInfo(e);
                log.info(stackTraceInfo);
                return "";
            }
        }

        /**
         * 分段解密
         *
         * @param privateKey 私钥
         * @param str        密文
         * @param split      密文分割标识符号
         * @return
         */
        public static String privateKeyDecryption(PrivateKey privateKey, String str, String split) {
            try {
                // 返回UTF-8编码的解密信息
                StringBuffer sb = new StringBuffer();
                for (String entity : str.split(split)) {
                    String s = privateKeyDecryption(privateKey, entity);
                    sb.append(s);
                }
                return sb.toString();
            } catch (Exception e) {
                String stackTraceInfo = ExceptionUtil.getStackTraceInfo(e);
                log.info(stackTraceInfo);
                return "";
            }
        }

        /**
         * 对打乱的密码的ASCII码根据key的hash种子生成的置换表还原密码
         *
         * @param str
         * @param key
         * @return
         */
        public static String passwordHashUnShuffle(String str, String key) {
            if (str == null || str.isEmpty()) return str;

            byte[] keyBytes = getHash(key); // 获取密钥的哈希值
            long seed = bytesToLong(keyBytes); // 将字节数组转换为整数种子
            int[] shuffleTable = generateShuffleTable(str.length(), seed); // 生成置换表
            int[] reverseTable = new int[shuffleTable.length];
            for (int i = 0; i < shuffleTable.length; i++) {
                reverseTable[shuffleTable[i]] = i;
            }

            char[] chars = str.toCharArray();
            char[] result = new char[chars.length];

            for (int i = 0; i < chars.length; i++) {
                result[i] = chars[reverseTable[i]];
            }

            return new String(result);
        }

        /**
         * 对密码的ASCII码根据key的hash种子生成的置换表打乱密码
         *
         * @param password
         * @param key
         * @return
         */
        public static String passwordHashShuffle(String password, String key) {
            String restPassword = "";
            /*
            1,获取password的长度,最小长度是16.
            2,根据key生成hash值,获取hash对应的随机数种子
            3,根据随机数种子,生成密码长度下的随机数,并依次存储到置换表数组中
            4,根据置换表转换密码,在根据9527截取密码,在执行+-+运算,然后取绝对值+运算值的长度
            5.返回
             */
            if (StringUtils.isEmpty(password) || password.length() <= 16) {
                restPassword = "";
            } else {
                byte[] hash = getHash(key);// 获取密钥的哈希值
                long seed = bytesToLong(hash);// 将字节数组转换为整数种子
                int[] shuffleTable = generateShuffleTable(password.length(), seed);//生成置换表
                String string = Arrays.toString(shuffleTable);
                System.out.println("shuffleTable:" + string);
                //将密码ASCII转字符数组
                char[] chars = password.toCharArray();
                //还原的密码字符数组
                char[] result = new char[chars.length];
                for (int i = 0; i < chars.length; i++) {
                    //根据置换表还原
                    result[i] = chars[shuffleTable[i]];
                }
                restPassword = new String(result);
            }
            return restPassword;
        }

        /**
         * 生成key的哈希值
         * MessageDigest是Java中用于计算加密哈希值的工具，
         * 通过使用不同的哈希算法，可以生成不同长度的哈希值。
         * digest()方法是其中的一个核心方法
         *
         * @param key
         * @return
         */
        public static byte[] getHash(String key) {
            try {
                //创建 MessageDigest 对象，使用 SHA-256 算法
                MessageDigest md = MessageDigest.getInstance("SHA-256");
                //调用 digest() 方法计算哈希值
                return md.digest(key.getBytes());
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException("Failed to get SHA-256 hash", e);
            }
        }

        /**
         * 将字节数组转换为整数种子
         *
         * @param bytes
         * @return
         */
        public static long bytesToLong(byte[] bytes) {
            long result = 0;
            for (int i = 0; i < 4; i++) { // 只取前4个字节
                //2进制 << 左移8位数
                //bytes[i] & 0xFF 确保 bytes[i] 的值在0到255之间。这是因为对于负数，Java使用补码表示法，而 & 0xFF 可以将其转换为对应的无符号值。
                result = (result << 8) | (bytes[i] & 0xFF);
            }
            return result;
        }

        /**
         * 根据种子生成对应的置换表
         *
         * @param length 置换表的长度
         * @param seed   种子
         * @return 置换表
         */
        public static int[] generateShuffleTable(int length, Long seed) {
            //置换表
            int[] table = new int[length];
            for (int i = 0; i < length; i++) {
                table[i] = i;
            }
            //创建伪随机
            Random random = new Random(seed);
            // 使用Fisher-Yates洗牌算法
            for (int i = length - 1; i > 0; i--) {
                //创建随机值 长度小于置换表的长度
                int j = random.nextInt(i + 1);
                System.out.println("j:" + j);
                //随机交换置换表的值，刷新
                int temp = table[i];
                table[i] = table[j];
                table[j] = temp;
            }
            return table;
        }

        private static final java.util.Base64.Encoder BASE64_ENCODER = java.util.Base64.getEncoder();

        public static void mainA(String[] args) {
            try {
//                InputStream inputStream = ClassLoader.getSystemResourceAsStream("fd-alias.keystore");
//                //设置 Java密钥库(Java Key Store，JKS)
//                KeyStore jks = KeyStore.getInstance("JKS");
//                // stream : 加载密钥存储库的输入流，password：用于检查密钥存储库完整性的密码，所使用的密码（生成fd-alias.keystore密码库密码）
//                jks.load(inputStream, "1024=0x400".toCharArray());
//                //关闭流程
//                inputStream.close();
//                //String alias 密码库的别名, char[] password 密钥的密码
//                PrivateKey privateKey = (PrivateKey) jks.getKey("fd-alias", "1024=0x400".toCharArray());
//                PublicKey publicKey = jks.getCertificate("fd-alias").getPublicKey();
//
//                // 保存公钥到文件
//                FileOutputStream publicFos = new FileOutputStream("public_key.key");
//                publicFos.write(publicKey.getEncoded());
//                publicFos.close();
//
//                // 保存私钥到文件
//                FileOutputStream privateFos = new FileOutputStream("private_key.key");
//                privateFos.write(privateKey.getEncoded());
//                privateFos.close();
//                String priPath = "jwt/private_key.key";
//                String pubPath = "jwt/public_key.key";
                String priPath = "jwt/rsa_private_key.key";
                String pubPath = "jwt/rsa_public_key.key";
//
                PrivateKey privateKey = getPrivateKey(priPath);
                PublicKey publicKey = getPublicKey(pubPath);
//                byte[] bytes = Base64.encodeBase64(publicKey.getEncoded());
                String publicKeyS = BASE64_ENCODER.encodeToString(publicKey.getEncoded());
                String privateKeyStr = (new BASE64Encoder()).encodeBuffer(privateKey.getEncoded()).replace("\r", "").replace("\n", "");
                String publicKeyStr = (new BASE64Encoder()).encodeBuffer(publicKey.getEncoded()).replace("\r", "").replace("\n", "");
                String array = "[\n" +
                        "    \"37Fj2n4yjAjW5HUnrP3woHqfSuGiuQXWyjBC5Cx2HP0SnZa6tEJoS4nRY0ceRZvgv2ZPnBVTQGj7vtR0XqFdOtd2noHtux8tqr5ZzsZAsC2SpKtfVW4aPTp3akfvds5VSfQw80i+erCAqUcwkuL379e983y2Em8cFdZmGF7EJBs=\",\n" +
                        "    \"pOPnSCEtNN5rKJdhwDDMQZiby8Hv6FoCDfi8UCXgi4Zi4/qyqdnZHyItSVbXR8jqaFRqhgxBnXciDJ+873YjG107KosPWaBCCihtr3lmV4fE9RUw6OwvDCmuy3D9Oq1bTU6PpgU4cbgb7l1WyY9spoaj/6NRLsIKi6yAeyhS0gs=\",\n" +
                        "    \"X9ixBGJartpOiT4g63UnrkTtTMYvCAWBKySp84DedCVW/qhxuHOiRRhwsIMlDNrso29kDQFuhw2s+7Ig5d0g49TTjL9gkZ7+nupkaPue7PSWlfCnT3aHNMt9MAvjC9VoqcnxyXQmS08kfvi1WNk4Bqnd9R8EL8uQQdjt6RVqMu4=\",\n" +
                        "    \"QMPly/fHRuVnb+Syk4exHIYdk7WFVCzG/rHMNGP7v9ay62gyQnKEYm83pqgyEUd548QZVHuY8/JB8h2M8MgMSOnXMH4LqbRPUygZ6TYKSYHRYLjjIcOrYVknMUBs1LVAhYoqRdqHIXdy8ZAi0s+hQWh9yCZFunydx7mntiRgeKk=\",\n" +
                        "    \"g2ovdOzOBpSNi2A6hbXH6SpPQLZopg2i8l2IIzyiFBoOm88FpVDc0ZvEDSfLzRz+nEM2BSG0mHDwESF0OBAL92+PGXQ7thq3FSP3sbyUfoDj7oLdj9WI82YNJzJZedVKOuf/0k2YC9zOUwXERnTrlR4RqXLfqgwW9Gd8Ol9qYYk=\",\n" +
                        "    \"ojP94VUVuEoTgnFXboQoxWv57nKkoDjtUYiXQQVH9h1VqjimIm8DvoCWAjWERf14SEat1LnnEDpO/mjTQG22wS/1bQuDKXs2746Gq/WoqsWziXvQ6/IWdSg5RLPQjQq9MmeUZL+VxquQtCOBMjUJld7yfr2YuF/hsw7DZ0TDh+s=\",\n" +
                        "    \"QfgAqCZwwQ45VAeOCmtfG8KZ6FU5LRIrfKAKpr31BZ2KgdkoRc3uXB7khQyaKdfo0J8b0/Oxbu1spRaTrk+C8FpXcjBwC772E6NW/kM5G1uH6T5g4f/l/e2Wj/dlz4IoKRy0DH0OZAuOoBxHLvAsy7Ie4eRwonBhUdU9KUrIing=\",\n" +
                        "    \"3H22kuguZVlOIjTbLKJY2FP8HCj+W7A3vtzhnoaSS6FJbomHvubiqpxCP38rP/iA3zDKeef4UYBuKAASTOlZ61B/Io61B58G4BS86I/qgJbt9LNgaR9PvhHYSRDETEwQ4w7mPbmMWuQFAHvzlJTKt6vl088tnJMJUFmD10058HI=\",\n" +
                        "    \"CIL3Xfdu97QXW+7FrUasnMHFhKByLq3Ou0gBnZB1nQriZRlQ+5NlV11LvTkD+e+eWd20rZJ5cAG+A2MrE3KzGXMFiAp3m8QWqP89rbVLS/hj3+RNEPM/iJSK84UjvKSiv4ypNie2LrNJjJs2WLIzWHPdagsMQeBIFN5db9u8Xt8=\",\n" +
                        "    \"5yWzSZGkitd1OPypPEy3j0MNAWv5gLk1kAbKfEIiq2QSsCmZR7nHsZ+9PtqyWapz0BBMyv53LoTMd8zOEFqdWcb2EIK8kkFYVMtIcli1MzCwqiUNSRaZ0Jji+lWObJA7vf67u5OR/Ym9W5MtyIJ7YWelixu0ALcbqXY2wmqjvGE=\",\n" +
                        "    \"y454FVpNmrVODt3G5CNxXHJzzSp1u5N7iZRuJ/gpsAIZYu/wU6ZbwE7NJ4KD3nWin6XRlzGw3ZwGqViFVgr5w82rKA9b2X1o0jQv6f1DnK6PQzzW4IpSDJyO4ox8c0InObB0DULrYyF34mmGeMQEg9mdWN1R5BkKv/r0W5gAJ7c=\"\n" +
                        "]";

                List<String> strings = JSON.parseArray(array, String.class);
//                byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
                Map<String, Object> testMap = new HashMap<>();
                testMap.put("name", "zhans");

                String s = JSON.toJSONString(testMap);
                Cipher cipher = Cipher.getInstance("RSA");
                cipher.init(Cipher.DECRYPT_MODE, privateKey);
                String outStr = new String();
                String data = "x21AN3ARbs1j6uMYE8zBxgIFWaFQJwAgPWxjzSgBrqJnaE3jZB8FwTxTnzvs+lzVoMvQ0oWQU3UE8umpd/uNqy9bqY86jAMfD7BSO3SockfvoMCD55a0Krl7JSbK/dABtLOTUundK9dmbOB/HPJOBUX9EYHEY2/e0D2bihXps0I=";

                cipher.init(Cipher.ENCRYPT_MODE, publicKey);
                String outStrA = Base64.encodeBase64String(cipher.doFinal(s.getBytes("UTF-8")));

                System.out.println("加密：" + outStrA);
                cipher.init(Cipher.DECRYPT_MODE, privateKey);
                byte[] bytes = cipher.doFinal(Base64.decodeBase64(outStrA.getBytes("UTF-8")));
                System.out.println("解密:" + new String(bytes));
                String cipherText = "";
                try {
                    StringBuffer addData = new StringBuffer("");
                    String[] data_sign = data.split("\\_");
//                    String[] data_sign = strings.toArray(new String[strings.size()]);
                    for (String sign : data_sign) {
                        byte[] decode2 = new byte[0];
                        try {
//                            decode2= EncryptUtil.decryptByPrivateKey(org.apache.commons.codec.binary.Base64.decodeBase64(sign), org.apache.commons.codec.binary.Base64.decodeBase64(privateKeyStr));
                            decode2 = cipher.doFinal(Base64.decodeBase64(sign.getBytes("UTF-8")));
//                            decode2 = RSACoder.decryptByPrivateKey(org.apache.commons.codec.binary.Base64.decodeBase64(sign), org.apache.commons.codec.binary.Base64.decodeBase64(privateKey));
                        } catch (Exception e) {
                            CommUtil.ExceptionUtil.getStackTraceInfo(e);
                        }
                        addData.append(new String(decode2));
                    }
                    //解析后的值
                    cipherText = new String(Base64.decodeBase64(addData.toString()));
                    System.out.println("base64Todata:" + cipherText);

                } catch (Exception e) {
                    CommUtil.ExceptionUtil.getStackTraceInfo(e);
                }
                Map<String, Object> map1 = JSON.parseObject(cipherText, new TypeReference<Map<String, Object>>() {
                });

//                String decrypt = EncryptUtil.decrypt(str, privateKeyStr);


                System.out.println("outStr:" + map1);
                System.out.println("==============");
                System.out.println("privateKeyStr:" + privateKeyStr);
                System.out.println("============================");
                System.out.println("publicKeyStr:" + publicKeyStr);
                System.out.println("============================");
                System.out.println("s:" + publicKeyS);
//                String strKey = encryptBASE64(publicKey.getEncoded());
                Map<String, Object> userMap = new HashMap<>();
                userMap.put("name", "张三");
                userMap.put("pwd", "123");
                String compactJws = Jwts.builder().claim("userInfo", JSON.toJSONString(userMap))
                        .setExpiration(new Date(new Date().getTime() + (1000 * 60 * 120)))
                        .signWith(SignatureAlgorithm.RS256, privateKey)
                        .compact();
                System.out.println("compactJws:" + compactJws);
                System.out.println();
            } catch (Exception e) {

                ExceptionUtil.getStackTraceInfo(e);
            }
        }

        public static void mainB(String[] args) {
            PrivateKey privateKey = getPrivateKey();
            PublicKey publicKey = getPublicKey();
            //加密
            Map<String, Object> userInfoMap = new HashMap<>();
            userInfoMap.put("name", "用户名");
            userInfoMap.put("age", 12);
            userInfoMap.put("phone", "12547884854");
            userInfoMap.put("email", "1257854@163.com");
            userInfoMap.put("password", "password");
            userInfoMap.put("roles", Arrays.asList("1", "2", "3", "4"));
            String jsonString = JSON.toJSONString(userInfoMap);
            String s = publicKeyEncryption(publicKey, jsonString, 117, "_*_");
            System.out.println("加密:" + s);
            String s1 = privateKeyDecryption(privateKey, s, "_*_");
            System.out.println("解密:" + s1);
        }

        public static void main(String[] args) {

        }
    }

    /**
     * 异常工具类
     */
    public static class ExceptionUtil {
        /**
         * 获取e.printStackTrace()的具体信息，赋值给String 变量，并返回
         *
         * @param e Exception
         * @return e.printStackTrace() 中 的信息
         */
        public static String getStackTraceInfo(Exception e) {
            StringWriter sw = null;
            PrintWriter pw = null;
            try {
                sw = new StringWriter();
                pw = new PrintWriter(sw);
                //将出错的栈信息输出到printWriter中
                e.printStackTrace(pw);
                pw.flush();
                sw.flush();
                return sw.toString();
            } catch (Exception ex) {
                return "发生错误";
            } finally {
                if (sw != null) {
                    try {
                        sw.close();
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                }
                if (pw != null) {
                    pw.close();
                }
            }
        }
    }

    /**
     * 集合工具类
     */
    public static class ListUtil {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            Map<String, Object> map = new HashMap<>();

            ArrayList<String> listA = new ArrayList<>();

        }


        /**
         * 获取两个集合的重复的，并替换
         *
         * @param baseList  基础集合
         * @param mergeList 子集合
         * @param key       对的key
         * @return 全新的基础集合
         */
        public static List<Map<String, Object>> mergeList(List<Map<String, Object>> baseList, List<Map<String, Object>> mergeList,
                                                          String key) {
            Map<Object, Map<String, Object>> collect = mergeList.stream().collect(Collectors.toMap(en -> en.get(key), en -> en));
            List<Map<String, Object>> restList = new ArrayList<>();
            for (Map<String, Object> map : baseList) {
                Object value = map.get(key);
                Map<String, Object> entityMap = collect.get(value);
                if (MapUtil.isEmpty(entityMap)) {
                    restList.add(map);
                } else {
                    restList.add(entityMap);
                }
            }
            return restList;

        }


    }

    public static class JasyptUtil {
        public static void main(String[] args) {

            //0JGr7xD5chMx4U//e2lrwSdwVgNXwisg
            EnvironmentPBEConfig config = new EnvironmentPBEConfig();

            StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
            encryptor.setPassword("dsd87@xGy6H-=1JGb47G2H72"); // 设置加密密码


            System.out.println(encryptor.decrypt("0JGr7xD5chMx4U//e2lrwSdwVgNXwisg"));
            System.out.println(encryptor.decrypt("R0dTWR/SbJgURLvPpIeCdpIJiaq+fEAnCHig0MBlnYNLadoq3GEd+QAP/VnsZS/N0P60f6kTwc4NWuxC8uOSFP4/L7lDTs57dyJzgBau7hJVcMAq3Fil+bhCbwh7Y7jI"));
            System.out.println(encryptor.decrypt("UUzidHB6wIVCN+MBotMThA=="));

        }
    }


    /**
     * map工具类
     */
    public static class MapUtils {
        public static boolean isEmpty(Map<?, ?> map) {
            return null == map || map.isEmpty();
        }

        public static boolean isNotEmpty(Map<?, ?> map) {
            return null != map && !map.isEmpty();
        }

        public static <K, V> HashMap<K, V> of(K key, V value) {
            HashMap<K, V> map = new HashMap<>();
            map.put(key, value);
            return map;
        }
    }



}
